/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package raytracer;

/**
 *
 * @author ferco
 * Vlastni trida pro praci s vektorem. Umoznuje normalizovat vektor, vzpocitat
 * skalarni a vektorovy soucin, nasobeni vectoru realnym cislem, odecteni a
 * pricteni dvou vektoru a odrazeni vektoru.
 */
public class Vector3D {

    //deklarace promennych
    private double x,y,z; //prvky vektoru

    /**
      *Konstruktor vektoru bez parametru. Vsechny slozky vektoru nastavi na 0
      *
     */
    public Vector3D(){

        this.x = 0;
        this.y = 0;
        this.z = 0;
    }

     /**
      *Konstruktor vektoru se zadanymi slozkami vektoru
      *
      *@param x prvni slozka vektoru
      *@param y druha slozka vektoru
      *@param z treti slozka vektoru
     */
    public Vector3D (double x, double y, double z){

        this.x = x;
        this.y = y;
        this.z = z;
    }

    /**
     *Normalizuje vektor. Stare hodnoty vektoru se nahradi novymi,
     * normalizovanymi
     */
    public void normalize(){

        // vypocet magnitude nutne pro normalizovani vektoru
        double magnitude = Math.sqrt((this.x * this.x) +
                                     (this.y * this.y) +
                                     (this.z * this.z));

        //normalizovani jednotlivych slozek vektoru
        this.x /= magnitude;
        this.y /= magnitude;
        this.z /= magnitude;
    }

    /**
      *Vypocita skalarni soucin vektoru
      *
      *@param vectB je vektor se kterym se soucin pocita
      *@return skalarni soucin
     */
    public double dot(Vector3D vectB){

        //vypocet jednotlivych slozek soucinu
        double a = this.x * vectB.getX();
        double b = this.y * vectB.getY();
        double c = this.z * vectB.getZ();

        // vrat skalarni soucin
        return a + b + c;
    }


    /**
      *Vypocita skalavektorovy soucin vektoru
      *
      *@param vectB je vektor se kterym se soucin pocita
      *@return vektorovy soucin soucin
     */
    public Vector3D cross (Vector3D vectB){

        //vypocet jednotlivych slozek soucinu
        double a = this.y * vectB.getZ() - this.z * vectB.getY();
        double b = this.z * vectB.getX() - this.x * vectB.getZ();
        double c = this.x * vectB.getY() - this.y * vectB.getX();

        // vrat skalarni soucin
        return new Vector3D(a,b,c);
    }

    /**
      *Vynasobi vektor s s realnzm cislem n a vrati vektor
      *
      *@param n je realne cislo,kterym nasobime kazdou slozku vektoru
      *@return vznasobeny vektor
     */
    public Vector3D multiplyBy(double n){

        //vypocet jednotlivych slozek soucinu
        double a = this.x * n;
        double b = this.y * n;
        double c = this.z * n;

        // vrat vznasobeny vektor
        return new Vector3D(a, b, c);
    }

    /**
      *Odecte od vektoru vektor B a vrati vektor
      *
      *@param vectB je vektor ktery budem odecitat
      *@return odecteny vektor
     */
    public Vector3D minusVector(Vector3D vectB){

        //vypocet jednotlivych slozek rozdilu
        double a = this.x - vectB.getX();
        double b = this.y - vectB.getY();
        double c = this.z - vectB.getZ();

        //vrat odecteny vektor
        return new Vector3D(a, b, c);
    }

    /**
      *Pricte k vektoru vektor B a vrati vektor
      *
      *@param vectB je vektor ktery budem pricitat
      *@return secteny vektor
     */
    public Vector3D plusVector(Vector3D vectB){

        //vypocet jednotlivych slozek souctu
        double a = this.x + vectB.getX();
        double b = this.y + vectB.getY();
        double c = this.z + vectB.getZ();

        //vrat secteny vektor
        return new Vector3D(a, b, c);
    }

    /**
      *odrazi vektor kolem normaly a vrati odrazeny vektor
      *
      *@param normal je normala kolem ktere bude vektor odrazen
      *@return odrazeny vektor
     */
    public Vector3D reflectVector(Vector3D normal){

        //vypocet skalaroveho soucinu
        double dot = this.dot(normal);

        //vypocet jednotlivych slozek odrazeneho vektoru
        ////vectorToLight.minusVector(normal.multiplyBy(2*vectorToLight.dot(normal)))
        double a = -this.x + (2 * normal.getX() * dot);
        double b = -this.y + (2 * normal.getY() * dot);
        double c = -this.z + (2 * normal.getZ() * dot);

        //vrat odrazeny vektor
        //return new Vector3D(a,b,c);
        return this.minusVector(normal.multiplyBy(2*dot));
    }

    @Override
    public String toString() {
        return "("+this.x + ", "+this.y+", "+this.z+")";
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Vector3D other = (Vector3D) obj;
        if (this.x != other.x) {
            return false;
        }
        if (this.y != other.y) {
            return false;
        }
        if (this.z != other.z) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 59 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
        hash = 59 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
        hash = 59 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
        return hash;
    }


    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

    public double getZ() {
        return z;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }

    public void setZ(double z) {
        this.z = z;
    }

    
}
